/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
// -*- coding:iso-8859-15 mode: c++ -*-
#ifndef JOYSTICK_CONTROL_H
#define JOYSTICK_CONTROL_H

#include <wx/event.h>
#include <wx/joystick.h>
#include <wx/arrstr.h>
#include <wx/gauge.h>
#include <wx/choice.h>
#include <wx/checkbox.h>

#include "JoystickControl_form.h"

#include "JoystickControlUtils.h"
#include "JoystickControlEvent.h"
#include "JoystickControlComponent.h"
#include "JoystickControlSettingsHandler.h"

// Components includes.
#include "JCMaCIJointGroupControl.h"
#include "JCMaCISpeedCtrl.h"

class CGIMILoader;

// Pair containing boolean for status if joysticks events should be read and
// the joystick object.
typedef std::pair< JoystickStatus, wxJoystick* > t_joystickReference;

// Vector containing the joystick objects. Generated at start.
typedef std::vector< t_joystickReference > t_joystickList;
typedef t_joystickList::iterator t_joystickListIterator;
typedef t_joystickList::const_iterator t_joystickListConstIterator;

class JoystickReaderThread : public wxThread {
  public:
    JoystickReaderThread(t_joystickList *joystickList,
                          wxWindow *tWindow);
    void* Entry();

    int addJoystickToRead(const int joystickId);
    int removeJoystickToRead(const int joystickId);

    /**
     * \param[in] joystickId Id-number of target joystick.
     * \return Number of axis on success, or JOYSTICKCONTROL_ERROR if joystick
     * was not found.
     */
    int getNumberAxesOnJoystick(const int joystickId) const;

    /**
     * \param[in] joystickId Id-number of target joystick.
     * \return Number of buttons on success, or JOYSTICKCONTROL_ERROR if
     * joystick was not found.
     */
    int getNumberButtonsOnJoystick(const int joystickId) const;
    
    /**
     * \param joystickId Id-number of target joystick.
     * \param axisId Id-number of the target axis.
     * \param inversion New inversion status for joystick.
     * \return JOYSTICKCONTROL_OK or JOYSTICKCONTROL_ERROR.
     */
     int setAxisInversion(const int joystickId,
                          const int axisId,
                          const bool inversion);

  private:
    // Disable copy-constructors.
    JoystickReaderThread(JoystickReaderThread& original);
    const JoystickReaderThread& operator= (const JoystickReaderThread& right);
  
    /**
     * Tries to send a wxEVT_GIMUI_JOYSTICK_MOVE event with the specified
     * parameters.
     * 
     * \param joystickId Number of the joystick.
     * \param current Current joystick status.
     * \param axisId Identifier for axis placed in m_zPosition.
     * \return JOYSTICKCONTROL_OK for success or JOYSTICKCONTROL_ERROR for
     * failure.
     */
    int sendMoveEvent(const int joystickId,
                      const JoystickStatus &current,
                      const int axisId);
    
    /**
     * Tries to send a wxEVT_GIMUI_JOYSTICK_BUTTON_UP or DOWN event with the
     * specified parameters.
     *
     * \param joystickId Number of the joystick.
     * \param current Current joystick status.
     * \param buttonDownEvent True if event to send should be down, false for up.
     * \param changedButton Number of the button that this event is related to.
     * \return JOYSTICKCONTROL_OK for success or JOYSTICKCONTROL_ERROR for
     * failure.
     */
    int sendButtonEvent(const int joystickId,
                        const JoystickStatus &current,
                        const bool buttonDownEvent,
                        const int changedButton);
    
    /**
     * Converts received axis-value to a float between -1.00 and 1.00 with
     * 0 being the center.
     */
    float convertAxisValueToFloat(const int axisValue,
                                  const int maxValue,
                                  const int minValue) const;
  
    t_joystickList *joysticks;
    wxWindow *targetWindow;
};

struct PanelIdentification {
  public:
    PanelIdentification();
    PanelIdentification(const int jId,
                        const int t,
                        const int i);
                        
    int joystickId;
    int type; ///< 1 for axis, 2 for button
    int id;
};

struct JoystickAxisPanel {
  public:
    JoystickAxisPanel(wxGauge *aGauge,
                      wxChoice *aChoice,
                      wxCheckBox *aBox,
                      const int aJoystickId,
                      const int aType,
                      const int aId);
    ~JoystickAxisPanel();
    
    wxGauge *axisGauge;
    wxChoice *axisChoice;
    wxCheckBox *axisCheckBox;
    
    PanelIdentification *ident;
  
  private:
    // Disable copy-constructors.
    JoystickAxisPanel(JoystickAxisPanel& original);
    const JoystickAxisPanel& operator= (const JoystickAxisPanel& right);
};

struct JoystickButtonPanel {
    JoystickButtonPanel(wxStaticText *bName,
                        wxStaticText *bStatus,
                        wxChoice *bChoice,
                        const int aJoystickId,
                        const int aType,
                        const int aId);
    ~JoystickButtonPanel();
    
    wxStaticText *buttonName;
    wxStaticText *buttonStatus;
    wxChoice *buttonChoice;
    
    PanelIdentification *ident;
  private:
    // Disable copy-constructors.
    JoystickButtonPanel(JoystickButtonPanel& original);
    const JoystickButtonPanel& operator= (const JoystickButtonPanel& right);
};

typedef std::vector< JoystickAxisPanel* > t_axisPanels;
typedef t_axisPanels::iterator t_axisPanelsIterator;
typedef t_axisPanels::const_iterator t_axisPanelsConstIterator;

typedef std::vector< JoystickButtonPanel* > t_buttonPanels;
typedef t_buttonPanels::iterator t_buttonPanelsIterator;
typedef t_buttonPanels::const_iterator t_buttonPanelsConstIterator;

class JoystickAxes {
  public:
    JoystickAxes();
    ~JoystickAxes();

    int addAxis(wxGauge *aGauge,
                wxChoice *aChoice,
                wxCheckBox *aBox,
                const int joystickId,
                const int aId);
    
    int setAxisPosition(const int axisId,
                        const float axisValue);
    
    int setToChoice(const int axisId,
                    const int selectionId,
                    const bool invert);
    
  private:
    t_axisPanels axisPanels;
};

class JoystickButtons {
  public:
    JoystickButtons();
    ~JoystickButtons();
    
    int addButton(wxStaticText *bName,
                  wxStaticText *bStatus,
                  wxChoice *bChoice,
                  const int joystickId,
                  const int bId);
    
    int setButtonPosition(const int buttonId,
                          const bool position);
    
    int setToChoice(const int buttondId,
                    const int selectionId);
  
  private:
    t_buttonPanels buttonPanels;
};

typedef std::map< int, PanelIdentification > t_identMap;
typedef t_identMap::iterator t_identMapIterator;
typedef t_identMap::const_iterator t_identMapConstIterator;
typedef std::pair< int, PanelIdentification > t_identMapPair;

/**
 *
 */
class AxisIdentifications {
  public:
    AxisIdentifications();
    ~AxisIdentifications();
    
    /**
     * Add new ident to map.
     */
    int addIdent(const int wxId,
                 const int joystickId,
                 const int axisId);
                 
    /**
     * Adds new ident to map and returns id that references to it.
     */
    int addNewAxis(const int joystickId,
                   const int axisId);
    
    /**
     * Find joystickId and axisId based on wxId (identifies checkBox)
     */
    int findIdent(const int wxId,
                  PanelIdentification &foundIdent);
  
  private:
    t_identMap idents;
    int freeId;
};

/**
 * \class CJoystickInformation
 * Stores the UI-objects for a joystick.
 */
class JoystickInformationPanel {
  public:

    /**
     * Constructor.
     * \param mWindow Reference to mainwindow.
     * \param pSizer Reference to sizer in which the information panel
     * should be drawn.
     * \param jId JoystickId
     * 
     */
    JoystickInformationPanel(wxPanel *mWindow,
                             wxSizer *pSizer,
                             const int jId);

    ~JoystickInformationPanel();

    /**
     * Initializes the information panel based on joystick information
     * (draws it).
     *
     * \param nAxes Number of axes in joystick.
     * \param nButtons Number of buttons in joystick.
     * \param axisSelections Possible options for an axis.
     * \param buttonSelections Possible options for a button.
     * \return errorCode
     */
    int initialize(const int nAxes,
                   const int nButtons,
                   const wxArrayString &axisSelections,
                   const wxArrayString &buttonSelections,
                   AxisIdentifications &axisReferences,
                   const wxArrayString &configurations);
    
    int setAxisToChoice(const int axisId,
                        const int selectionId,
                        const bool invert);
    
    int setButtonToChoice(const int buttonId,
                          const int selectionId);

  private:
    // No need to disable copy-constructors.
    //JoystickInformationPanel(JoystickInformationPanel& original);
    //const JoystickInformationPanel& operator= (const JoystickInformationPanel& right);
    
    wxPanel *mainWindow;
    wxPanel *currentPanel;

    wxSizer *parentSizer;
    
    int joystickId;
  public:
    JoystickAxes joystickAxes;
    JoystickButtons joystickButtons;
    PanelIdentification choiceIdent;
};

// Typedefs for search structure to component/event references.
typedef std::vector< JoystickControlComponent* > t_componentList;
typedef t_componentList::iterator t_componentListIterator;
typedef t_componentList::const_iterator t_componentListConstIterator;

/**
 * \struct OptionInformation
 */
struct OptionInformation {
  OptionInformation(const int aComponentId,
                    const int aOptionId,
                    const std::string aOptionDescription);
  int componentId;
  int optionId;
  std::string optionDescription;
};

typedef std::vector< OptionInformation > t_selectOptionList;
typedef t_selectOptionList::iterator t_selectOptionListIterator;
typedef t_selectOptionList::const_iterator t_selectOptionListConstIterator;

class JoystickComponentContainer {
  public:
    JoystickComponentContainer();
    ~JoystickComponentContainer();
    
    /**
     * Forwards event to component(s) that want it. (Activated components)
     * \return Number of components that wanted event.
     */
    int forwardEvent(const GIMUIJoystickEvent &event);
    
    /**
     * Adds component to components-list. Must not be null.
     */
    int addComponent(JoystickControlComponent* comp);
    
    /**
     * Activate all components.
     * 
     * \return Number of activated components.
     */
    int activateAllComponents();
    
    /**
     * Activate components that correspond to given GIMIService.
     * (Currently compares receive/accept and majortype.)
     * 
     * \return Number of activated components.
     */
    int activateComponents(gimi::GIMIService &target);
    
    /**
     * List components.
     * \param onlyActive If true, list only active components.
     */
    int listComponents(std::vector<std::string> &componentNames,
                       bool onlyActive = false);
    
    /**
     * Refresh axis/button optionlists. Run after components have been added
     * and activated.
     * Options are added only from activated components.
     */
     int refreshOptionLists();
    
    /**
     * Gets a combined list of axis-options from all components.
     */
    int listAxisOptions(wxArrayString &allAxisOptions) const;
    
    /**
     * Gets a combined list of axis-options from components.
     */
    int listAxisOptions(std::vector< t_stringPair > &axisOptions,
                        bool onlyActive = false) const;
    
    /**
     * Gets a combined list of button-options from active components.
     */
    int listButtonOptions(wxArrayString &allButtonOptions) const;
    
    /**
     * Gets a combined list of button-options from components.
     */
    int listButtonOptions(std::vector< t_stringPair > &buttonOptions,
                          bool onlyActive = false) const;
    
    int findAxisIdFor(const std::string componentName,
                      const std::string axisName);
    
    int findButtonIdFor(const std::string componentName,
                        const std::string buttonName);
    
    /**
     * Binds certain joystick-axis to specified option.
     */
    int setAxisOption(const int selectionId,
                      const int joystickId,
                      const int axisId);
    
    /**
     * Binds certain joystick-button to specified option.
     */
    int setButtonOption(const int selectionId,
                        const int joystickId,
                        const int buttonId);
    
  private:
    // Disable copy-constructors.
    JoystickComponentContainer(JoystickComponentContainer& original);
    const JoystickComponentContainer& operator= (const JoystickComponentContainer& right);
    
    t_componentList components;
    t_selectOptionList axisOptions; ///< Axis-options from all components
    t_selectOptionList buttonOptions; ///< Button-options from all components
};

// Map containing information panels (UI-elements) for currently selected joysticks.
// Int is the selection-id of joystick (place in joystickList vector.)
typedef std::map< int, JoystickInformationPanel > t_joystickPanels;
typedef t_joystickPanels::iterator t_joystickPanelsIterator;
typedef t_joystickPanels::const_iterator t_joystickPanelsConstIterator;
typedef std::pair< int, JoystickInformationPanel > t_joystickPanelsPair;

class JoystickControl : public wxPanel
{
public:
  JoystickControl(gimi::GIMI *aGIMIPtr, 
                  wxWindow *ParentWindow);
  ~JoystickControl();
  
  /**
   *
   */
  bool SetTargetServices(const std::vector<gimi::GIMIService> &aGIMIServiceArray);
  

  /**
   * Handle incoming message from GIMI.
   */
  void OnReceive(int r, gimi::GIMIMessage *message);

  /**
   * Handle connection event.
   */
  void OnConnect();

  /**
   * Handle disconnection event.
   */
  void OnDisconnect();

  DECLARE_EVENT_TABLE()

    private:
  // Disable copy-constructors.
  JoystickControl(JoystickControl& original);
  const JoystickControl& operator= (const JoystickControl& right);
  /**
   * Handle events when a joystick is selected in listbox.
   */
  void OnSelectJoystick(wxCommandEvent &event);
    
  /**
   * Handle events when a component selection is made for different
   * axes & buttons.
   */
  void OnSelectFeatureComponent(wxCommandEvent &event);

  /**
   * Handle checkbox-events (invert axis)
   */
  void OnSelectInvertAxis(wxCommandEvent &event);

  /**
   * Handle event from a joystick.
   */
  void OnJoystickEvent(wxEvent &event);

  JoystickReaderThread *joystickReaderThread; ///< Thread that reads
  ///< joystick-positions etc.
  ///< and sends events.
  t_joystickPanels joystickPanels; ///< UI Components for selected joysticks.
  JoystickComponentContainer components; ///< Controller components.
  AxisIdentifications axisIdents; ///< References wxId's to certain axes in certain joysticks.

  gimi::GIMI *iGIMIPtr;
  std::vector<gimi::GIMIService> iTargetServices;
  wxFrame *iParentFrame;

  //// Settings
  JoystickControlSettingsHandler *settingsHandler;

  JoystickControlPanel mainPanel; ///< Panel that contains this component.
};

#endif //JOYSTICK_CONTROL_H
